package com.lichuang.fruitvarietysm.user.controller;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lichuang.fruitvarietysm.user.entity.User;
import com.lichuang.fruitvarietysm.user.finalproperties.AccountCode;
import com.lichuang.fruitvarietysm.user.finalproperties.EmailType;
import com.lichuang.fruitvarietysm.user.mapper.UserMapper;
import com.lichuang.fruitvarietysm.user.util.MD5Util;
import com.lichuang.fruitvarietysm.user.util.ValidatorUtil;
import com.lichuang.fruitvarietysm.user.util.SmsUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * <p>
 * 用户表 前端控制器
 * </p>
 *
 * @author shuaihang
 * @since 2019-05-15
 */
@RestController
@RequestMapping("//user")
//@CrossOrigin(value = "http://localhost:63342", allowCredentials = "true")
public class UserController {

    /**
     * 库存什么时候扣
     * 订单号怎么生成
     * 服务如何控制权限
     * sku
     * 如何控制重复下单
     * 分布式事务
     * 是否要冗余字段
     */

    @Autowired
    private UserMapper userMapper;

//    @Autowired
//    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Resource(name = "emailoutput")
    private MessageChannel emailoutput;

    /**
     * 发送验证码
     * 生成6位随机数，存入redis缓存中
     * 返回结果是JSONString，Code为OK，代表成功，No代表失败；Message代表信息
     *
     * @param phone
     */
    @PostMapping("/sendSms")
    public String sendSms(String phone) {
        // 生成6为随机数
        Integer code = (int) ((Math.random() * 9 + 1) * 100000);
        // key 的规则"code::xxxxxxxxxxx"
        String key = "code::" + phone;
        // 验证码的过期时间，分钟为单位
        long timeoutOfMinutes = 100000;
        // 如果redis中没有对应的key就存入缓存，并返回true；如果已经存在则不保存，并返回false
        boolean codeTag = redisTemplate.opsForValue().setIfAbsent(key, code.toString(), Duration.ofMinutes(timeoutOfMinutes));
        // 如果为false，表示缓存中已经存在该值，提示用户
        if (!codeTag) {
            Long ttl = redisTemplate.getExpire(key);
            Map<String, String> map = new HashMap<>();
            map.put("Code", "No");
            map.put("message", "请于" + ttl + "秒后再发送验证码！");
            return JSON.toJSONString(map);
        }
        // 发送短信，并返回结果
        return SmsUtil.sendSms(phone, code).getData();
    }

    /**
     * 验证验证码
     *
     * @param phone
     * @param newCode
     * @return map，包含如下Key：
     * {code}为OK代表成功，No代表失败
     * {message}为信息
     */
    @PostMapping("/validationCode")
    public Map validationCode(String phone, Integer newCode) {
        Map<String, Object> map = new HashMap<>();
        // 从redis中读取验证码
        String code = redisTemplate.opsForValue().get("code::" + phone).toString();
        if (code == null || "".equals(code)) {
            map.put("code", AccountCode.NO);
            map.put("message", "请先发送验证码！");
            return map;
        }
        if (Integer.parseInt(code) != newCode) {
            map.put("code", AccountCode.NO);
            map.put("message", "验证码不正确！");
            return map;
        }
        map.put("code", AccountCode.OK);
        map.put("message", "验证码正确！");
        return map;
    }

    /**
     * @param user
     * @return map，包含如下Key：
     * {code}为OK代表成功，No代表失败
     * {message}为信息
     */
    @PostMapping("/registry")
    public Map registry(User user) {

        Map<String, Object> map = new HashMap<>();

        //判断是否勾选协议
//        if (!protocol) {
//            map.put("code", AccountCode.NO);
//            map.put("message", "用户未勾选协议");
//            return map;
//        }

        //验证邮箱是否正确，true为正确，false不正确
//        String userEmail = user.getUserEmail();
//        boolean emailTag = ValidatorUtil.emailMatches(userEmail);
//        if (!emailTag) {
//            map.put("code", AccountCode.NO);
//            map.put("message", "邮箱格式不正确");
//            return map;
//        }

        // 生成当前日期，添加到实体类中
        user.setCreateTime(LocalDateTime.now());

        // MD5加密用户密码
        String md5pwd = MD5Util.MD5Encode(user.getUserPassword(), "UTF-8", false);
        user.setUserPassword(md5pwd);

        // 将数据插入数据库，受影响行数
        int insertNum = userMapper.insert(user);
        if (insertNum == 0) {
            map.put("code", AccountCode.NO);
            map.put("message", "插入失败");
            return map;
        }

        // 通知Kafka发送邮件,emailType为要发送邮件的类型
//        Message message = MessageBuilder.withPayload(user).setHeader("emailType", EmailType.REGISTRY).build();
//        emailoutput.send(message);

        map.put("code", AccountCode.OK);
        map.put("message", "注册成功");
        map.put("user", user);
        return map;
    }

    /**
     * 密码登录
     *
     * @param username 可以是用户名，邮箱，手机号
     * @param password
     * @return code：请求状态码{@link AccountCode},message：信息描述
     */
    @PostMapping("/login")
    public Map login(String username, String password, String url, HttpServletResponse response) {
        // 用来判断的实体类
        User userParam = new User();

        // 使用MD5加密后与数据库对比
        String md5pwd = MD5Util.MD5Encode(password, "UTF-8", false);
        userParam.setUserPassword(md5pwd);

        if (ValidatorUtil.emailMatches(username)) {
            // 如果是邮箱
            userParam.setUserEmail(username);
        } else if (ValidatorUtil.phoneMatches(username)) {
            // 如果是手机号
            userParam.setUserPhone(username);
        } else {
            // 否则就是用户名
            userParam.setUserName(username);
        }

        QueryWrapper<User> queryWrapper = new QueryWrapper<>(userParam);
        // 根据实体类条件来查询一条记录
        User user = userMapper.selectOne(queryWrapper);

        Map<String, Object> map = new HashMap<>();
        if (user == null) {
            map.put("code", AccountCode.UNAUTHORIZED);
            map.put("message", "密码和账户名不匹配");
            return map;
        }
        if (user.getUserStatus() == 2) {
            map.put("code", AccountCode.LOCKED);
            map.put("message", "你的账户被锁定");
            return map;
        }

        // 登录成功后
        map.put("code", AccountCode.OK);
        map.put("message", "登录成功");
        map.put("user", user);
        map.put("url", url);

        // 将登录状态存入session中
//        saveUserWithSession(user, map, request);

        // 将登录状态存入redis中
        saveUserWithRedis(user, response);


        // 将username存入cookie中
        Cookie cookie = new Cookie("username", username);
        // 设置cookie的过期时间为3天
        cookie.setMaxAge(259200);
        cookie.setPath("/");
        response.addCookie(cookie);

        return map;
    }

    /**
     * 在redis中保存用户的登录状态
     *
     * @param user
     * @param response
     */
    public void saveUserWithRedis(User user, HttpServletResponse response) {
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        String key = "user::" + token;
        // 超时时间
        long timeOut = 3;
        // 将token存入redis
        redisTemplate.opsForValue().set(key, user, Duration.ofDays(timeOut));
        Cookie cookie = new Cookie("loginToken", token);
        // 设置cookie的过期时间为3天
        cookie.setMaxAge(259200);
        cookie.setPath("/");
        response.addCookie(cookie);
    }

    @RequestMapping("/getCurrentUser")
//    @Cacheable(cacheNames = "user",key = "#loginToken")
    public User getCurrentUser(String loginToken) {
        User user = (User) redisTemplate.opsForValue().get("user::" + loginToken);
        if (null == user) {
            return null;
        }
        return user;
    }

    /**
     * 拿到当前用户信息（本服务使用）
     * 最终还是调用{@link UserController#getCurrentUser(String)}
     *
     * @param loginToken
     * @return
     */
    @RequestMapping("/getCurrentUserInfo")
    public User getCurrentUserInfo(@CookieValue("loginToken") String loginToken) {
        return getCurrentUser(loginToken);
    }

    /**
     * 从cookie中读取最后一次登录的用户名
     *
     * @return
     */
    @RequestMapping("/readUsernameInCookie")
    public String readUsernameInCookie(@CookieValue(value = "username") String username) {
        if (username == null || username.equals("")) {
            return "";
        }
        return username;
    }

    /**
     * 将用户登录状态存入session中
     *
     * @param user
     * @param map
     * @param request
     */
//    public void saveUserWithSession(User user, Map map, HttpServletRequest request) {
//        HttpSession session = request.getSession();
//        session.setAttribute("isLogin", true);
//        session.setAttribute("userId", user.getUserId());
//        int maxInactiveInterval = 60 * 60 * 12;
//        // 设置此session的过期时间
//        session.setMaxInactiveInterval(maxInactiveInterval);
//        // 存入当前session的信息
//        map.put("creationTime", session.getCreationTime());
//        map.put("lastAccessedTime", session.getLastAccessedTime());
//        map.put("sesionId", session.getId());
//        map.put("maxInactiveInterval", session.getMaxInactiveInterval());
//    }

    /**
     * 从session中读取用户登录状态
     * 判断是否登录，没有登录返回-1，如果是登录状态则返回userId
     *
     * @param request
     * @return
     */
//    @RequestMapping("/readUserInSession")
//    public Integer readUserInSession(HttpServletRequest request) {
////        Map<String, Object> map = new HashMap<>();
//        HttpSession session = request.getSession();
//        /**
//         * 1. session中的isLogin是否为true
//         * 2. 为true是登录的状态，为null或者false是没有登录
//         */
//        Boolean isLogin = (Boolean) session.getAttribute("isLogin");
//        if (isLogin == null || isLogin == false) {
////            map.put("code", AccountCode.UNLOGIN);
////            map.put("message", "session过期或未登录");
//            return -1;
//        }
//        Integer userId = (Integer) session.getAttribute("userId");
////        map.put("code", AccountCode.OK);
////        map.put("message", "登录状态！");
////        map.put("userId",userId);
//        return userId;
//    }

//    @RequestMapping("/showUserInfo")
//    public User showUserInfo(HttpServletRequest request) {
//        // 查看用户的登录状态
//        Integer userId = readUserInSession(request);
//        if (userId == -1) {
//            // 没有登录
//            return null;
//        }
//        User user = readUserWithRedis(userId);
//        return user;
//    }
    @PostMapping("/phoneExists")
    public Map phoneExists(String phone) {
        Map<String, Object> map = new HashMap<>();
        User userParam = new User();
        userParam.setUserPhone(phone);
        QueryWrapper queryWrapper = new QueryWrapper(userParam);
        Integer count = userMapper.selectCount(queryWrapper);
        if (count >= 1) {
            User user = userMapper.selectOne(queryWrapper);
            map.put("code", AccountCode.NO);
            map.put("user", user);
            map.put("message", "此手机号已被注册");
            return map;
        }
        map.put("code", AccountCode.OK);
        map.put("message", "没有重复的手机号");
        return map;
    }

    @PostMapping("/userNameExists")
    public Boolean userNameExists(String userName) {
        User userParam = new User();
        userParam.setUserName(userName);
        QueryWrapper queryWrapper = new QueryWrapper(userParam);
        Integer count = userMapper.selectCount(queryWrapper);
        if (count >= 1) {
            // 用户名已存在
            return false;
        }
        return true;
    }

    /**
     * 通过redis读取用户信息，如果redis中不存在则查询数据库，并把返回值添加到redis中
     *
     * @param userId
     * @return
     */
//    @Cacheable(cacheNames = "user", key = "#userId")
//    public User readUserWithRedis(Integer userId) {
//        return userMapper.selectById(userId);
//    }

    /**
     * 更新用户信息，并更新redis中的值
     *
     * @param user
     * @return
     */
    @CachePut(cacheNames = "user", key = "#token")
    public User updateUserWithRedis(User user, String token) {
        // 更新修改的时间，每次调用此方法都会更新时间
        user.setUpdateTime(LocalDateTime.now());
        userMapper.updateById(user);
        // 更新后查询最新的数据存入redis，如果将方法参数中的user直接返回，会造成缓存中的信息不完整
        return userMapper.selectById(user.getUserId());
    }


}
